#ifndef _BUFFER_H_
#define _BUFFER_H_

#include <stdio.h>
#include <assert.h>

#ifndef _linux_
#include <windows.h>
#endif

#include "data_define/type_define.h"
#include "GeneralException.h"
#include "Int128.h"

#define ALLOC(p,size) ((p)->_Alloc((size), __FILE__, __LINE__))

// Represent a buffer in memory.
class tBuffer 
{
public:
	PBYTE pBuf;				// Pointer to memory address.
	size_t size;				// buffer size allocated.

	bool attached;			// flag to delete the buffer 
							// when instance will be deleted.

	tBuffer();
	virtual ~tBuffer();

	//----------------------------------------------------------------
	// Associate this instance to specified memory data. Attach to a 
	// memory space means you can access the memory space via this
	// instance and when this instance goes off, the memory data will
	// be automatically freed. If you attach to another buffer instance,
	// that instance is not be affected.
	//----------------------------------------------------------------
	// Parameters:
	//     data : the memory space where to be attached.
	//     s    : the size of memory space.
	//     buf  : the other buffer instance whose memory space will be
	//            attached by this instance.
	// Exception:
	//     E_MEMATTACH_FAIL : Can not attach to specified data.
	//----------------------------------------------------------------
	void attach(PBYTE data, size_t s);
	void attach(const tBuffer & buf);
	//----------------------------------------------------------------

	//----------------------------------------------------------------
	// Associate this instance to specified memory data. This function
	// will not attach to that memory space, so it will not free the 
	// memory sapce when it's destoryed.
	//----------------------------------------------------------------
	// Parameters:
	//     Same as "attach" functions.
	// Return Values:
	//     Same as "attach" functions.
	//----------------------------------------------------------------
	void refto(PBYTE data, size_t s);
	void refto(const tBuffer & buf);
	//----------------------------------------------------------------

	//-------------------------------------
	// remove the reference of memory data.
	//-------------------------------------
	void deref();
	//-------------------------------------

	//---------------------------------------------------------
	// set attached flag to off, so when instance is destoryed, 
	// the memory data will not be freed.
	//---------------------------------------------------------
	void detach();
	//---------------------------------------------------------

	// Get size fo allocated or refered data.
	size_t Size() const;

	//-----------------------------------------------------------------
	// Copy data to instance's memory space.
	//-----------------------------------------------------------------
	// Parameters:
	//	   offset    : Specify the offset place where to place the data.
	//	   data      : The start address to copy.
	//	   data_size : Size to coping.
	// Return Value:
	//     data size this function copied.
	// Exception:
	//     E_MEMCOPY_OVERFLOW : This instance have not enough space to
	//                          store the specified data.
	//-----------------------------------------------------------------
	size_t Copy(size_t offset, PBYTE data, size_t data_size);
	//-----------------------------------------------------------------

	//---------------------------------------------------------------
	// Copy from other instance. This function will free the previous
	// memory space and allocate a new space for new data.
	//---------------------------------------------------------------
	// Parameters:
	//     buf : The source buffer instance.
	// Return Value:
	//     Same as above "Copy" function.
	// Exception:
	//     Same as above "Copy" function.
	//---------------------------------------------------------------
	size_t Copy(const tBuffer & buf, const char * file, int line);
	//---------------------------------------------------------------

	//---------------------------------------------------------------
	// Copy from other instance, with offset specifing the place to
	// Receive the data.
	//---------------------------------------------------------------
	// Parameters:
	//     offset : Specify the offset from instance's memory space.
	//     buf    : The source buffer instance.
	// Return Value:
	//     Same as "Copy" function.
	// Exception:
	//     Same as above "Copy" function.
	//---------------------------------------------------------------
	size_t Copy(size_t offset, tBuffer & buf);
	//---------------------------------------------------------------

	//---------------------------------------------------------------
	// Allocate the memory space with size s.
	//---------------------------------------------------------------
	// Parameters:
	//     s : Size of the memory to allocate.
	// Return Value:
	//     s  : This function is successed.
	// Exception:
	//     E_MEMALLOC_FAIL  : Can not allocate memory from system.
	//     E_MEMALLOC_AGAIN : This instance have the allocated space.
	//---------------------------------------------------------------
	size_t _Alloc(size_t s, const char * file, int line);
	//---------------------------------------------------------------

	//----------------------------------------------------------
	// Free the allocated space. And this instance is no longer
	// refer to this space.
	//----------------------------------------------------------
	void DeAlloc();
	void DeAllocIfAttached();
	//----------------------------------------------------------

	//-------------------------------------
	// Get base memory address of the data.
	//-------------------------------------
	PBYTE GetBase() const;
	//-------------------------------------
	
	//----------------------------------------------------------
	// Dump the data refered by this instance to file. This is 
	// only used for debugging.
	//----------------------------------------------------------
	// Parameters:
	//     offset : From where to dump out.
	//     len    : How many byte to dump.
	//     fn     : File name.
	//     flag   : File open flag.
	//----------------------------------------------------------
	void Dump(size_t offset, int len, TCHAR* fn, int flag);
	//----------------------------------------------------------
};

// A buffer class when support stream reading and writing.
class tStreamBuffer : public tBuffer
{
public:
	size_t usedsize;				// size of readed or writed.
								// Also considered as access pointer.

	tStreamBuffer();

	//----------------------------------------
	// Override base class function.
	// Free the memory space of stream buffer.
	//----------------------------------------
	void DeAlloc();
	//----------------------------------------

	//---------------------------------------------------------------
	// Append data to tail of current data in instance. In fact, it
	// call the base class' "Copy" function. 
	//---------------------------------------------------------------
	// Paramters:
	//     data      : the pointer of data to be copied.
	//     data_size : The size to be copied.
	// Return Value:
	//     Same as base class' "Copy" functions.
	// Exception:
	//     Same as base class' "Copy" functions.
	//---------------------------------------------------------------
	size_t Append(const PBYTE data, size_t data_size);
	size_t Append(tBuffer & buf);
	//---------------------------------------------------------------

	//---------------------------------------------------------------
	// Check how many data can read or write. Be careful to the return
	// value of it. If it's minus value, it means a error on this 
	// instance, maybe memory space write overflow happend before.
	//---------------------------------------------------------------
	size_t Left();
	//---------------------------------------------------------------

	//---------------------------------
	// Reset access point to beginning.
	//---------------------------------
	int ResetPointer();
	//---------------------------------

	//---------------------------------------------------------------
	// Set access point to specified place.
	//---------------------------------------------------------------
	// Paramters:
	//     pos : where the pointer will point to, from beginning.
	// Exception:
	//     E_SETAP_FAIL : Can not set the access point.
	//---------------------------------------------------------------
	void SetPointer(size_t pos);
	//---------------------------------------------------------------

	//---------------------------------------------------------------
	// Return the current memory address of AP is pointing to.
	//---------------------------------------------------------------
	PBYTE CurrentPointer();
	//---------------------------------------------------------------

	//---------------------------------------------------------------
	// Return the current access point. You can also consider it as 
	// the used data size.
	//---------------------------------------------------------------
	size_t Offset();
	//---------------------------------------------------------------

	//---------------------------------------------------------------
	// Foward / Backward the access pointer.
	//---------------------------------------------------------------
	// Parameters:
	//     step : How many step to move.
	// Exception:
	//     E_SETAP_FAIL : Can not set the access point.
	//---------------------------------------------------------------
	void MovePointer(size_t step);
	void MoveBackPointer(size_t step);
	//---------------------------------------------------------------

	//---------------------------------------------------------------
	// Read a regular data from current access point.
	// GetByte() return a byte value, GetShort() return a word value,
	// GetInt3() return a 24 bits value, GetInt4() return a 32 bits
	// value, GetInt8() return a 64 bits value.
	// All value is regarded as a big endian in memory space.
	//---------------------------------------------------------------
	// Return Value:
	//     The value of reading from current access point.
	// Excetpion:
	//     E_MEMREAD_OVERFLOW : Can not read more data from instance.
	//---------------------------------------------------------------
	BYTE    GetByte();
	USHORT  GetShort();
	UINT    GetInt3();
	UINT    GetInt4();
	UINT64 GetInt8();
	tInt128 GetDataInt128();
	//---------------------------------------------------------------

	//---------------------------------------------------------------
	// Write a byte data to current access point.
	//---------------------------------------------------------------
	// Exception:
	//     E_MEMCOPY_OVERFLOW : have not space to write data.
	//---------------------------------------------------------------
	void WriteByte(BYTE v);
	void WriteShort(USHORT v);
	void WriteInt3(UINT v);
	void WriteInt4(UINT v);
	void WriteInt8(UINT64 v);
	void WriteDataInt128(tInt128 v);
	//---------------------------------------------------------------

	//---------------------------------------------------------------
	// Get a block of data from the instance's memory space.
	//---------------------------------------------------------------
	// Parameters:
	//     pData : source data pointer.
	//     _size : Size of the data.
	// Exception:
	//     E_MEMREAD_OVERFLOW : have not space to so much data.
	//---------------------------------------------------------------
	void GetData(PBYTE pData, size_t _size);
	//---------------------------------------------------------------
};

// Virtual interface for persistence.
class ifPersist
{
	virtual int Load(tStreamBuffer & sbBuffer) = 0;
	virtual int Save(tStreamBuffer & sbBuffer) = 0;
	virtual size_t GetSize() = 0;
};


////////////////////////////////////////////
// define the inline functions.
////////////////////////////////////////////

inline tBuffer::tBuffer(): pBuf(NULL), size(0), attached(false)
{
}

inline tBuffer::~tBuffer()
{
	if (pBuf && attached)
	{
		delete[] pBuf;
	}
}

inline void tBuffer::attach(PBYTE data, size_t s)
{
	if (!attached)
	{
		pBuf = data;
		size = s;
		attached = true;
	}
	else
		throw GeneralException(E_MEMATTACH_FAIL);
}

inline void tBuffer::attach(const tBuffer & buf)
{
	return attach(buf.pBuf, buf.size);
}

inline void tBuffer::refto(PBYTE data, size_t s)
{
	attach(data,s);
	attached = false;
}

inline void tBuffer::refto(const tBuffer & buf)
{
	attach(buf);
	attached = false;
}

inline void tBuffer::deref()
{
	attached = false;
	size = 0;
	pBuf = 0;
}

inline void tBuffer::detach()
{
	attached = false;
}

inline size_t tBuffer::Size() const
{
	return size;
}

inline PBYTE tBuffer::GetBase() const
{
	return pBuf;
}

inline tStreamBuffer::tStreamBuffer() : usedsize(0)
{
}

inline size_t tStreamBuffer::Append(PBYTE data, size_t data_size)
{
	size_t r = Copy(usedsize, data, data_size);
	usedsize += r;
	return r;
}

inline size_t tStreamBuffer::Append(tBuffer & buf)
{
	size_t r = Copy(usedsize, buf);
	usedsize += r;
	return r;
}

inline void tStreamBuffer::DeAlloc()
{
	usedsize = 0;
	tBuffer::DeAlloc();
}

inline size_t tStreamBuffer::Left()
{
	return (size - usedsize);
}

inline int tStreamBuffer::ResetPointer()
{
	usedsize = 0;
	return 0;
}

inline void tStreamBuffer::SetPointer(size_t pos)
{
	assert(pos < size);
	if (pos < size)
	{
		usedsize = pos;
	}
	else
		throw GeneralException(E_SETAP_FAIL);
}

inline PBYTE tStreamBuffer::CurrentPointer()
{
	return pBuf + usedsize;
}

inline size_t tStreamBuffer::Offset()
{
	return usedsize;
}

inline void tStreamBuffer::MovePointer(size_t step)
{
	if (usedsize + step <= size && 
		usedsize + step >= 0)
	{		
		usedsize += step;
	}
	else
	{
		assert(false);
		throw GeneralException(E_SETAP_FAIL);
	}
}

inline void tStreamBuffer::MoveBackPointer(size_t step)
{
	if (usedsize - step <= size && 
		usedsize - step >= 0)
	{		
		usedsize -= step;
	}
	else
	{
		assert(false);
		throw GeneralException(E_SETAP_FAIL);
	}
}

inline BYTE tStreamBuffer::GetByte()
{
	if (usedsize + 1 <= size)
	{
		usedsize++;
		return pBuf[usedsize - 1];
	}
	else
	{
		assert(false);
		throw GeneralException(E_MEMREAD_OVERFLOW);
	}
	return 0;
}

inline void tStreamBuffer::WriteByte(BYTE v)
{
	if (usedsize + 1 <= size)
	{
		pBuf[usedsize++] = v;
	}
	else
	{
		assert(false);
		throw GeneralException(E_MEMCOPY_OVERFLOW);
	}
}

inline USHORT tStreamBuffer::GetShort()
{
	if (usedsize + 2 <= size)
	{
		USHORT v = (pBuf[usedsize]<<8) | pBuf[usedsize+1];
		usedsize += 2;
		return v;
	}
	else
	{
		assert(false);
		throw GeneralException(E_MEMREAD_OVERFLOW);
	}
	return 0;
}

inline void tStreamBuffer::WriteShort(USHORT v)
{
	if (usedsize + 2 <= size)
	{
		for (int i = 1; i >= 0; i--)
		{
			pBuf[usedsize++] = (v>>(i*8)) & 0xff;
		}
	}
	else
	{
		assert(false);
		throw GeneralException(E_MEMCOPY_OVERFLOW);
	}
}

inline UINT tStreamBuffer::GetInt3()
{
	if (usedsize + 3 <= size)
	{
		UINT v = (pBuf[usedsize]<<16) | (pBuf[usedsize+1]) |
			pBuf[usedsize+2];
		usedsize += 3;
		return v;	
	}
	else
	{
		assert(false);
		throw GeneralException(E_MEMREAD_OVERFLOW);
	}
}

inline void tStreamBuffer::WriteInt3(UINT v)
{
	if (usedsize + 3 <= size)
	{
		for (int i = 2; i >= 0; i--)
		{
			pBuf[usedsize++] = (v>>(i*8)) & 0xff;
		}
	}
	else
	{
		assert(false);
		throw GeneralException(E_MEMCOPY_OVERFLOW);
	}
}	

inline UINT tStreamBuffer::GetInt4()
{
	if (usedsize + 4 <= size)
	{
		UINT v = (pBuf[usedsize]<<24) | (pBuf[usedsize+1]<<16) |
			(pBuf[usedsize+2]<<8) | pBuf[usedsize+3];
		usedsize += 4;
		return v;
	}
	else
	{
		assert(false);
		throw GeneralException(E_MEMREAD_OVERFLOW);
	}
	return 0;
}

inline void tStreamBuffer::WriteInt4(UINT v)
{
	if (usedsize + 3 <= size)
	{
		for (int i = 3; i >= 0; i--)
		{
			pBuf[usedsize++] = (v>>(i*8)) & 0xff;
		}
	}
	else
	{
		assert(false);
		throw GeneralException(E_MEMCOPY_OVERFLOW);
	}
}

inline UINT64 tStreamBuffer::GetInt8()
{
	if (usedsize + 8 <= size)
	{
		UINT64 v = 0;
		for (int i = 0; i < 8; i++)
		{
			v <<= 8;
			v |= pBuf[usedsize++];
		}
		return v;
	}
	else
	{
		assert(false);
		throw GeneralException(E_MEMREAD_OVERFLOW);
	}
	return 0;
}

inline void tStreamBuffer::WriteInt8(UINT64 v)
{
	if (usedsize + 8 <= size)
	{
		for (int i = 7; i >= 0; i--)
		{
			pBuf[usedsize++] = (BYTE)((v>>(i<<3)) & 0xff);
		}
	}
	else
	{
		assert(false);
		throw GeneralException(E_MEMCOPY_OVERFLOW);
	}
}

inline void tStreamBuffer::WriteDataInt128(tInt128 v)
{
	WriteInt8(v.m_High64);
	WriteInt8(v.m_Low64);
}

inline tInt128 tStreamBuffer::GetDataInt128()
{
	tInt128 v;
	v.m_High64 = GetInt8();
	v.m_Low64 = GetInt8();
	return v;
}
#endif
